#pragma once

// class declarations
class CogProductionPool;

// -----

#include "CogProduction.h"

// ------------------------------------------------------------------------------------------

class CogProductionPool
{
public:
	CogProductionPool(uint32_t _id = 0, CogProductionPool* _pParentPool = NULL);

	// inline methods:
	uint32_t getId() const { return poolId; }
	uint32_t getValidInstanceUsage() const { return totalInstanceUsage; }
	uint32_t getCurrentInstanceUsage() const { return currentInstanceUsage; }
	uint32_t getValidInstances() const { return validInstances; }
	uint32_t getTemplateUsage() const { return totalTemplateUsage; }
	bool isEmpty() const { return !instances.size() && !templates.size() && !pools.size(); }
	CogProductionPool& addNewPool() { return addNewPool(++maxId); }
	CogProduction& addNewInstance(uint32_t generation, bool initCache) { return addNewInstance(++maxId, generation, initCache); }
	CogProduction& addNewTemplate(uint32_t id) { templates.push_back(CogProduction(id, COGSF_NONE, this, NULL, 0)); return templates.back(); }
	CogProductionPool* getParent() { return pParentPool; }
	CogGrammar* getGrammar() { return pParentGrammar; }
	void use() { currentInstanceUsage++; if (pParentPool) pParentPool->use(); }
	void use(size_t times) { currentInstanceUsage += times; if (pParentPool) pParentPool->use(times); }
	void unuse() { currentInstanceUsage--; if (pParentPool) pParentPool->unuse(); }

	bool hasInstances() const;
	CogProductionPool& addNewPool(uint32_t id);
	CogProductionPool* getPool(uint32_t id);
	CogProduction& addNewInstance(uint32_t id, uint32_t generation, bool initCache);
	CogProduction& duplicateInstance(const CogProduction& original, uint32_t generation);
	void addInitialRule(const CogProduction& original);
			
	void computeInstanceUsage();
	void computeTemplateUsage();
	void clearAll();
	void clearInstances();
	void clearTemplates();
	void clearEmptyPools();
	void setGrammar(CogGrammar* pGram);
	size_t getInstanceSize() const;
	size_t getTemplateSize() const;
	CogProduction* getFirstLocalInstanceOfId(uint32_t id);
	CogProduction* getFirstLocalInstanceWithRHS(vector<CogSymbol>& rhs);
	CogProduction* getFirstLocalTemplate();
	CogProduction* getAnyInstance();
	CogProduction* getAnyInstance(uint32_t target);
	CogProduction* getAnyValidInstance(uint32_t target);
	CogProduction* getAnyTemplate();
	CogProduction* getAnyTemplate(uint32_t target);
	void removeUnusedInstances();

	size_t buildInstances();
	size_t buildTemplates();
	bool build(CogProduction& rule);
	void markConstTemplates();

	// label management:
	uint32_t addLabel(const string& label);
	void clearId();
	void clearLabels();
	bool getId(const string& label, uint32_t& id) const;
	string getLabel(uint32_t index) const;

	void ancestryToStream(uint32_t childId, ostream &out) const;
	void instancesToStream(ostream &out, const CogGrammar* pGram) const;
	void templatesToStream(ostream &out, const CogGrammar* pGram) const;
	void poolsToStream(ostream &out) const;

	friend class CogEvolver;

private:
	// data:
	uint32_t poolId;

	CogGrammar* pParentGrammar;
	CogProductionPool* pParentPool;

	list<CogProduction> instances;
	list<CogProduction> templates;
	list<CogProductionPool> pools;

	map<uint32_t, string> labelMap;
	uint32_t maxId;
	uint32_t validInstances;
	uint32_t totalInstanceUsage;
	uint32_t currentInstanceUsage;
	uint32_t totalTemplateUsage;
};

// ------------------------------------------------------------------------------------------
